/**
 * @license
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import '@polymer/iron-input/iron-input.js';
import '@polymer/paper-toggle-button/paper-toggle-button.js';
import '../../../styles/gr-form-styles.js';
import '../../../styles/gr-menu-page-styles.js';
import '../../../styles/gr-page-nav-styles.js';
import '../../../styles/shared-styles.js';
import {applyTheme as applyDarkTheme, removeTheme as removeDarkTheme} from '../../../styles/themes/dark-theme.js';
import '../../plugins/gr-endpoint-decorator/gr-endpoint-decorator.js';
import '../gr-change-table-editor/gr-change-table-editor.js';
import '../../shared/gr-button/gr-button.js';
import '../../shared/gr-date-formatter/gr-date-formatter.js';
import '../../shared/gr-diff-preferences/gr-diff-preferences.js';
import '../../shared/gr-page-nav/gr-page-nav.js';
import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js';
import '../../shared/gr-select/gr-select.js';
import '../gr-account-info/gr-account-info.js';
import '../gr-agreements-list/gr-agreements-list.js';
import '../gr-edit-preferences/gr-edit-preferences.js';
import '../gr-email-editor/gr-email-editor.js';
import '../gr-gpg-editor/gr-gpg-editor.js';
import '../gr-group-list/gr-group-list.js';
import '../gr-http-password/gr-http-password.js';
import '../gr-identities/gr-identities.js';
import '../gr-menu-editor/gr-menu-editor.js';
import '../gr-ssh-editor/gr-ssh-editor.js';
import '../gr-watched-projects-editor/gr-watched-projects-editor.js';
import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
import {PolymerElement} from '@polymer/polymer/polymer-element.js';
import {htmlTemplate} from './gr-settings-view_html.js';
import {getDocsBaseUrl} from '../../../utils/url-util.js';
import {ChangeTableMixin} from '../../../mixins/gr-change-table-mixin/gr-change-table-mixin.js';

const PREFS_SECTION_FIELDS = [
  'changes_per_page',
  'date_format',
  'time_format',
  'email_strategy',
  'diff_view',
  'publish_comments_on_push',
  'work_in_progress_by_default',
  'default_base_for_merges',
  'signed_off_by',
  'email_format',
  'size_bar_in_change_table',
  'relative_date_in_change_table',
];

const GERRIT_DOCS_BASE_URL = 'https://gerrit-review.googlesource.com/' +
    'Documentation';
const GERRIT_DOCS_FILTER_PATH = '/user-notify.html';
const ABSOLUTE_URL_PATTERN = /^https?:/;
const TRAILING_SLASH_PATTERN = /\/$/;

const HTTP_AUTH = [
  'HTTP',
  'HTTP_LDAP',
];

/**
 * @extends PolymerElement
 */
class GrSettingsView extends ChangeTableMixin(GestureEventListeners(
    LegacyElementMixin(
        PolymerElement))) {
  static get template() { return htmlTemplate; }

  static get is() { return 'gr-settings-view'; }
  /**
   * Fired when the title of the page should change.
   *
   * @event title-change
   */

  /**
   * Fired with email confirmation text, or when the page reloads.
   *
   * @event show-alert
   */

  static get properties() {
    return {
      prefs: {
        type: Object,
        value() { return {}; },
      },
      params: {
        type: Object,
        value() { return {}; },
      },
      _accountInfoChanged: Boolean,
      _changeTableColumnsNotDisplayed: Array,
      /** @type {?} */
      _localPrefs: {
        type: Object,
        value() { return {}; },
      },
      _localChangeTableColumns: {
        type: Array,
        value() { return []; },
      },
      _localMenu: {
        type: Array,
        value() { return []; },
      },
      _loading: {
        type: Boolean,
        value: true,
      },
      _changeTableChanged: {
        type: Boolean,
        value: false,
      },
      _prefsChanged: {
        type: Boolean,
        value: false,
      },
      /** @type {?} */
      _diffPrefsChanged: Boolean,
      /** @type {?} */
      _editPrefsChanged: Boolean,
      _menuChanged: {
        type: Boolean,
        value: false,
      },
      _watchedProjectsChanged: {
        type: Boolean,
        value: false,
      },
      _keysChanged: {
        type: Boolean,
        value: false,
      },
      _gpgKeysChanged: {
        type: Boolean,
        value: false,
      },
      _newEmail: String,
      _addingEmail: {
        type: Boolean,
        value: false,
      },
      _lastSentVerificationEmail: {
        type: String,
        value: null,
      },
      /** @type {?} */
      _serverConfig: Object,
      /** @type {?string} */
      _docsBaseUrl: String,
      _emailsChanged: Boolean,

      /**
       * For testing purposes.
       */
      _loadingPromise: Object,

      _showNumber: Boolean,

      _isDark: {
        type: Boolean,
        value: false,
      },
    };
  }

  static get observers() {
    return [
      '_handlePrefsChanged(_localPrefs.*)',
      '_handleMenuChanged(_localMenu.splices)',
      '_handleChangeTableChanged(_localChangeTableColumns, _showNumber)',
    ];
  }

  /** @override */
  attached() {
    super.attached();
    // Polymer 2: anchor tag won't work on shadow DOM
    // we need to manually calling scrollIntoView when hash changed
    this.listen(window, 'location-change', '_handleLocationChange');
    this.dispatchEvent(new CustomEvent('title-change', {
      detail: {title: 'Settings'},
      composed: true, bubbles: true,
    }));

    this._isDark = !!window.localStorage.getItem('dark-theme');

    const promises = [
      this.$.accountInfo.loadData(),
      this.$.watchedProjectsEditor.loadData(),
      this.$.groupList.loadData(),
      this.$.identities.loadData(),
      this.$.editPrefs.loadData(),
      this.$.diffPrefs.loadData(),
    ];

    promises.push(this.$.restAPI.getPreferences().then(prefs => {
      this.prefs = prefs;
      this._showNumber = !!prefs.legacycid_in_change_table;
      this._copyPrefs('_localPrefs', 'prefs');
      this._cloneMenu(prefs.my);
      this._cloneChangeTableColumns();
    }));

    promises.push(this.$.restAPI.getConfig().then(config => {
      this._serverConfig = config;
      const configPromises = [];

      if (this._serverConfig && this._serverConfig.sshd) {
        configPromises.push(this.$.sshEditor.loadData());
      }

      if (this._serverConfig &&
          this._serverConfig.receive &&
          this._serverConfig.receive.enable_signed_push) {
        configPromises.push(this.$.gpgEditor.loadData());
      }

      configPromises.push(
          getDocsBaseUrl(config, this.$.restAPI)
              .then(baseUrl => { this._docsBaseUrl = baseUrl; }));

      return Promise.all(configPromises);
    }));

    if (this.params.emailToken) {
      promises.push(this.$.restAPI.confirmEmail(this.params.emailToken).then(
          message => {
            if (message) {
              this.dispatchEvent(new CustomEvent('show-alert', {
                detail: {message},
                composed: true, bubbles: true,
              }));
            }
            this.$.emailEditor.loadData();
          }));
    } else {
      promises.push(this.$.emailEditor.loadData());
    }

    this._loadingPromise = Promise.all(promises).then(() => {
      this._loading = false;

      // Handle anchor tag for initial load
      this._handleLocationChange();
    });
  }

  /** @override */
  detached() {
    super.detached();
    this.unlisten(window, 'location-change', '_handleLocationChange');
  }

  _handleLocationChange() {
    // Handle anchor tag after dom attached
    const urlHash = window.location.hash;
    if (urlHash) {
      // Use shadowRoot for Polymer 2
      const elem = (this.shadowRoot || document).querySelector(urlHash);
      if (elem) {
        elem.scrollIntoView();
      }
    }
  }

  reloadAccountDetail() {
    Promise.all([
      this.$.accountInfo.loadData(),
      this.$.emailEditor.loadData(),
    ]);
  }

  _isLoading() {
    return this._loading || this._loading === undefined;
  }

  _copyPrefs(to, from) {
    for (let i = 0; i < PREFS_SECTION_FIELDS.length; i++) {
      this.set([to, PREFS_SECTION_FIELDS[i]],
          this[from][PREFS_SECTION_FIELDS[i]]);
    }
  }

  _cloneMenu(prefs) {
    const menu = [];
    for (const item of prefs) {
      menu.push({
        name: item.name,
        url: item.url,
        target: item.target,
      });
    }
    this._localMenu = menu;
  }

  _cloneChangeTableColumns() {
    let columns = this.getVisibleColumns(this.prefs.change_table);

    if (columns.length === 0) {
      columns = this.columnNames;
      this._changeTableColumnsNotDisplayed = [];
    } else {
      this._changeTableColumnsNotDisplayed = this.getComplementColumns(
          this.prefs.change_table);
    }
    this._localChangeTableColumns = columns;
  }

  _formatChangeTableColumns(changeTableArray) {
    return changeTableArray.map(item => {
      return {column: item};
    });
  }

  _handleChangeTableChanged() {
    if (this._isLoading()) { return; }
    this._changeTableChanged = true;
  }

  _handlePrefsChanged(prefs) {
    if (this._isLoading()) { return; }
    this._prefsChanged = true;
  }

  _handleRelativeDateInChangeTable() {
    this.set('_localPrefs.relative_date_in_change_table',
        this.$.relativeDateInChangeTable.checked);
  }

  _handleShowSizeBarsInFileListChanged() {
    this.set('_localPrefs.size_bar_in_change_table',
        this.$.showSizeBarsInFileList.checked);
  }

  _handlePublishCommentsOnPushChanged() {
    this.set('_localPrefs.publish_comments_on_push',
        this.$.publishCommentsOnPush.checked);
  }

  _handleWorkInProgressByDefault() {
    this.set('_localPrefs.work_in_progress_by_default',
        this.$.workInProgressByDefault.checked);
  }

  _handleInsertSignedOff() {
    this.set('_localPrefs.signed_off_by', this.$.insertSignedOff.checked);
  }

  _handleMenuChanged() {
    if (this._isLoading()) { return; }
    this._menuChanged = true;
  }

  _handleSaveAccountInfo() {
    this.$.accountInfo.save();
  }

  _handleSavePreferences() {
    this._copyPrefs('prefs', '_localPrefs');

    return this.$.restAPI.savePreferences(this.prefs).then(() => {
      this._prefsChanged = false;
    });
  }

  _handleSaveChangeTable() {
    this.set('prefs.change_table', this._localChangeTableColumns);
    this.set('prefs.legacycid_in_change_table', this._showNumber);
    this._cloneChangeTableColumns();
    return this.$.restAPI.savePreferences(this.prefs).then(() => {
      this._changeTableChanged = false;
    });
  }

  _handleSaveDiffPreferences() {
    this.$.diffPrefs.save();
  }

  _handleSaveEditPreferences() {
    this.$.editPrefs.save();
  }

  _handleSaveMenu() {
    this.set('prefs.my', this._localMenu);
    this._cloneMenu(this.prefs.my);
    return this.$.restAPI.savePreferences(this.prefs).then(() => {
      this._menuChanged = false;
    });
  }

  _handleResetMenuButton() {
    return this.$.restAPI.getDefaultPreferences().then(data => {
      if (data && data.my) {
        this._cloneMenu(data.my);
      }
    });
  }

  _handleSaveWatchedProjects() {
    this.$.watchedProjectsEditor.save();
  }

  _computeHeaderClass(changed) {
    return changed ? 'edited' : '';
  }

  _handleSaveEmails() {
    this.$.emailEditor.save();
  }

  _handleNewEmailKeydown(e) {
    if (e.keyCode === 13) { // Enter
      e.stopPropagation();
      this._handleAddEmailButton();
    }
  }

  _isNewEmailValid(newEmail) {
    return newEmail && newEmail.includes('@');
  }

  _computeAddEmailButtonEnabled(newEmail, addingEmail) {
    return this._isNewEmailValid(newEmail) && !addingEmail;
  }

  _handleAddEmailButton() {
    if (!this._isNewEmailValid(this._newEmail)) { return; }

    this._addingEmail = true;
    this.$.restAPI.addAccountEmail(this._newEmail).then(response => {
      this._addingEmail = false;

      // If it was unsuccessful.
      if (response.status < 200 || response.status >= 300) { return; }

      this._lastSentVerificationEmail = this._newEmail;
      this._newEmail = '';
    });
  }

  _getFilterDocsLink(docsBaseUrl) {
    let base = docsBaseUrl;
    if (!base || !ABSOLUTE_URL_PATTERN.test(base)) {
      base = GERRIT_DOCS_BASE_URL;
    }

    // Remove any trailing slash, since it is in the GERRIT_DOCS_FILTER_PATH.
    base = base.replace(TRAILING_SLASH_PATTERN, '');

    return base + GERRIT_DOCS_FILTER_PATH;
  }

  _handleToggleDark() {
    if (this._isDark) {
      window.localStorage.removeItem('dark-theme');
      removeDarkTheme();
    } else {
      window.localStorage.setItem('dark-theme', 'true');
      applyDarkTheme();
    }
    this._isDark = !!window.localStorage.getItem('dark-theme');
    this.dispatchEvent(new CustomEvent('show-alert', {
      detail: {
        message: `Theme changed to ${this._isDark ? 'dark' : 'light'}.`,
      },
      bubbles: true,
      composed: true,
    }));
  }

  _showHttpAuth(config) {
    if (config && config.auth &&
        config.auth.git_basic_auth_policy) {
      return HTTP_AUTH.includes(
          config.auth.git_basic_auth_policy.toUpperCase());
    }

    return false;
  }

  /**
   * Work around a issue on iOS when clicking turns into double tap
   */
  _onTapDarkToggle(e) {
    e.preventDefault();
  }
}

customElements.define(GrSettingsView.is, GrSettingsView);
